/*============================================================================
  File:      caseprocessor.cpp
 
  Summary:   Implementation of CASEPROCESSOR class that is used to extract
			 data from Analysis Services.
 
  Date:		 June 30, 2004
------------------------------------------------------------------------------
  This file is part of the Microsoft SQL Server Code Samples.
 
  Copyright (C) 2003 Microsoft Corporation.  All rights reserved.
 
This source code is intended only as a supplement to Microsoft
Development Tools and/or on-line documentation.  See these other
materials for detailed information regarding Microsoft code samples.
 
THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.
============================================================================*/

#include "stdafx.h"
#include "caseprocessor.h"
#include "DataValues.h"

CASEPROCESSOR::CASEPROCESSOR()
	: _vbCategorical(*this), _vdblValue(*this), _viAttribute(*this)
{
	_pcasereader = NULL;
}


HRESULT CASEPROCESSOR::Initialize(IDMAttributeSet*	pidmattributeset, 
							      IDMPushCaseSet*	pidmpushcaseset, 
								  CASEREADER*		pcasereader,
								  bool				bMissingAtRandom) 
{
	// Prepare the case processor to recieve calls to ProcessCase(). 

	ATLASSERT(pidmattributeset != NULL);
	ATLASSERT(pidmpushcaseset != NULL);
	ATLASSERT(pcasereader != NULL);

	if (pcasereader == NULL || pidmattributeset == NULL || pidmpushcaseset == NULL)
	{
		return E_POINTER;
	}

	_pcasereader		= pcasereader;
	_cAttribute			= 0;
	_spidmattributeset	= pidmattributeset;
	_viewtype			= _pcasereader->Viewtype();
	_bDensePushSet		= (pidmpushcaseset->IsDense() == S_OK) ? true : false;
	_bMissingAtRandom	= bMissingAtRandom;

	HRESULT hr = pidmattributeset->GetAttributeCount(&_cAttribute);

	RETURN_ON_FAIL(hr);
    
	// We allow all four possibilities of:
	//
	// (source=dense, reader=dense)
	// (source=dense, reader=sparse)
	// (source=sparse, reader=dense)
	// (source=sparse, reader=sparse)

	// The member _vdblValue is passed to the reader: if the reader is dense, this
	// vector contains the value for attribute i at position i; if the reader is
	// sparse, the vector contains the value for attribute _viAttribute[i] at 
	// position i.

	if (_viewtype == CASEREADER::viewtypeDense)
	{ 
		CHECK_STL_MEM(_vdblValue.resize(_cAttribute));

		if (_bDensePushSet == false)
		{
			// Initialize with all missing

			for (ULONG iAttribute = 0; iAttribute < _cAttribute; iAttribute++)
			{
				_vdblValue[iAttribute] = dblMissing;
			}
		}
	}

	CHECK_STL_MEM(_vbCategorical.resize(_cAttribute));

	for (ULONG iAttribute = 0; iAttribute < _cAttribute; iAttribute++)
	{
		DM_ATTRIBUTE_FLAGS	dmattributeflags;

		hr = pidmattributeset->GetAttributeFlags(iAttribute, &dmattributeflags);

		RETURN_ON_FAIL(hr);

		if (dmattributeflags & DMAF_DISCRETE)
		{
			_vbCategorical[iAttribute] = true;
		}
		else
		{
			_vbCategorical[iAttribute] = false;
		}
	}

	return S_OK;
}

CASEPROCESSOR::~CASEPROCESSOR()
{
}

// IDMCaseProcessor

STDMETHODIMP	CASEPROCESSOR::RequestCapacity(ULONG in_cCapacity)
{
	// This function should not be called.

	return E_FAIL;
}

STDMETHODIMP	CASEPROCESSOR::ProcessCase(DM_CaseID dmcaseid, 
										   ULONG	cValue, 
										   ULONG*	rgValue)
{
	ULONG	ulCaseID = (UINT) dmcaseid;

	// The input argument rgValue is a bit confusing: if the push set is dense, 
	// then in_rgValue is an array of DM_STATE_VALUE. Otherwise, rgValue is an array
	// of DM_ATTRIBUTE_VALUE.

	// Notes on implementing 4 combinations : 
	//
	// (source=dense, reader=dense) : simply copy the (transformed) source
	// data values into _vdblValue.
	//
	// (source=dense, reader=sparse) : traverse the (transformed) source data
	// values, and for every value that is not missing (=dblMissing), push on
	// the value and attribute index to _vdblValue and _viAttribute, respectively.
	//
	// (source=sparse, reader=dense) : _vdblValue in this case is initially filled
	// with missing. For each source attribute/value, fill in the appropriate value
	// in _vdblValue. After calling CASEREADER::ProcessCaseDense(), traverse the
	// set of provided attributes again and fill with missing so that _vdblValue once
	// again contiains all missing.
	//
	// (source=sparse, reader=sparse) : simply copy the attribute indices and transformed
	// values to _viAttribute and _vdblValue, respectively.

	HRESULT hr = S_OK;

	if (_bDensePushSet)
	{
		if (_viewtype == CASEREADER::viewtypeSparse)
		{
			CHECK_STL_MEM(_vdblValue.resize(0));
			CHECK_STL_MEM(_viAttribute.resize(0));
		}

		if (cValue != _cAttribute)
		{
			// For dense data, we should get a value for every attribute.

			return E_UNEXPECTED;
		}

		DM_STATE_VALUE* rgdmstatevalue	= (DM_STATE_VALUE*) rgValue;
		
		// The values are specified in order. That is, rgdmstatevalue[i] is
		// the value for the ith attribute.

		for (UINT iAttribute = 0; iAttribute < _cAttribute; iAttribute++)
		{
			DBL dblValue;

			if (_vbCategorical[iAttribute])
			{
				dblValue = (_bMissingAtRandom) 
					? ::DblCategoricalMAR(rgdmstatevalue[iAttribute]) 
					: ::DblCategorical(rgdmstatevalue[iAttribute]);
			}
			else
			{
				dblValue = ::DblContinuous(rgdmstatevalue[iAttribute]);
			}

			if (_viewtype == CASEREADER::viewtypeSparse)
			{
				if (dblValue != dblMissing)
				{
					CHECK_STL_MEM(_vdblValue.push_back(dblValue));
					CHECK_STL_MEM(_viAttribute.push_back(iAttribute));
				}
			}
			else
			{
				_vdblValue[iAttribute] = dblValue;
			}
		}

		if (_viewtype == CASEREADER::viewtypeSparse)
		{
			hr = _pcasereader->ProcessCaseSparse(ulCaseID, _viAttribute, _vdblValue);

			return hr;
		}
		else
		{
			hr = _pcasereader->ProcessCaseDense(ulCaseID, _vdblValue);

			return hr;
		}
	}
	else
	{
		// Sparse input data 

		DM_ATTRIBUTE_VALUE*	rgdmattributevalue = (DM_ATTRIBUTE_VALUE*) rgValue;

		if (_viewtype == CASEREADER::viewtypeSparse)
		{
			CHECK_STL_MEM(_vdblValue.resize(cValue));
		}

		// Always cache away the attribute indices. If we have a dense reader, we 
		// need this list to re-initialize to missing.

		CHECK_STL_MEM(_viAttribute.resize(cValue));

		for (UINT idmattributevalue = 0; idmattributevalue < cValue; idmattributevalue++)
		{
			UINT			iAttribute		= rgdmattributevalue[idmattributevalue].Attribute;
			DM_STATE_VALUE	dmstatevalue	= rgdmattributevalue[idmattributevalue].Value;
			DBL				dblValue;

			if (_vbCategorical[iAttribute])
			{
				dblValue = (_bMissingAtRandom) 
					? ::DblCategoricalMAR(dmstatevalue) 
					: ::DblCategorical(dmstatevalue);
			}
			else
			{
				dblValue = ::DblContinuous(dmstatevalue);
			}

			if (_viewtype == CASEREADER::viewtypeSparse)
			{
				_viAttribute[idmattributevalue]	= iAttribute;
				_vdblValue[idmattributevalue]	= dblValue;
			}
			else
			{
				ATLASSERT(_vdblValue[iAttribute] == dblMissing);

				_vdblValue[iAttribute] = dblValue;
			}
		}

		if (_viewtype == CASEREADER::viewtypeSparse)
		{
			hr = _pcasereader->ProcessCaseSparse(ulCaseID, _viAttribute, _vdblValue);

			return hr;
		}
		else
		{
			hr = _pcasereader->ProcessCaseDense(ulCaseID, _vdblValue);

			// Reinitialize dense values to missing

			for (UINT iValue = 0; iValue < cValue; iValue++)
			{
				_vdblValue[_viAttribute[iValue]] = dblMissing;
			}

			return hr;
		}
	}
}